#!/usr/bin/python
#coding:utf-8

import requests
import json
import traceback
from mybase.myconfig import GetConfig

# g_common.py
API_URL = "http://api.gizwits.com/app"
def render_url(url):
    return API_URL + url 

def auto_token(func):
    def _auto_token(*args, **kwargs):
        _self = args[0]    # param: self
        resp = func(*args, **kwargs) # 调用原function
        d = resp.json()
        try:
            _self = _self.set_token( d['token'] )
        except KeyError:
            print traceback.format_exc()
        return resp  # 返回原function结果
    return _auto_token

def auto_resp_json(func):
    def _auto_resp_json(*args, **kwargs):
        resp = func(*args, **kwargs) # 调用原function
        try:
            d = resp.json()
        except Exception:
            print traceback.format_exc()
        return d  # 返回json
    return _auto_resp_json

class GServiceClient(object):
    def __init__(self, appid):
        self.session = requests.Session()
        self.session.proxies = requests.compat.getproxies()
        self.token = None
        self.uid = None
        self.expire_at = None
        self.headers = {"Content-Type": "application/json"}
        self.headers.update( {'X-Gizwits-Application-Id': appid,} )

    def send_request(self, req, timeout=30):
        '''
            :param req: A prepared requests. type: class requests.Request
            :param timeout: Timeout duration in seconds. type: int
            :returns: response. type: dict
        '''
        req.headers = self.headers
        req.headers.update(self.session.headers)
        if req.data == []:
            # let the body clean.
            # req.data = json.dumps({})
            pass
        else:
            req.data = json.dumps(req.data)
        res = self.session.send(req.prepare(), timeout=timeout)
        return res

    def set_token(self, token):
        ''' Set token manually to avoid having to login repeatedly '''
        self.token = token
        self.headers["X-Gizwits-User-token"] = self.token

    def login(self, username, password):
        ''' 登陆
        username, email, phone
        '''
        req_body = {'username': username, 'password': password}
        req = requests.Request("POST", render_url('/login'), data=req_body)
        res = self.send_request( req )
        d = res.json()
        self.set_token( d['token'] )
        self.uid = d['uid']
        self.expire_at = d['expire_at']
        return d

    @auto_token
    def create_user_by_username(self, username, password):
        req_body = {
            'username': username,
            'password': password
            }
        req = requests.Request("POST", render_url('/users'), data=req_body)
        return self.send_request( req )

    @auto_token
    def create_user_by_email(self, email, password):
        req_body = {'email': email,
                        'password': password}
        req = requests.Request("POST", render_url('/users'), data=req_body)
        return self.send_request( req )

    @auto_token
    def create_user_by_phone(self, phone, password, code):
        req_body = {
            'phone': phone,
            'password': password,
            'code': code
        }
        req = requests.Request("POST", render_url('/users'), data=req_body)
        return self.send_request( req )

    @auto_token
    def anonymous_login(self, phone_id):
        req_body = {'phone_id': phone_id}
        req = requests.Request("POST", render_url('/users'), data=req_body)
        return self.send_request( req )

    # update
    def update_info(self, username, password):
        req_body = {'username': username, 'password': password}
        req = requests.Request("PUT", render_url('/users'), data=req_body)
        return self.send_request( req )

    def update_pwd(self, old_pwd, new_pwd):
        req_body = {
            "old_pwd": str(old_pwd),
            "new_pwd": str(new_pwd)
            }
        req = requests.Request("PUT", render_url('/users'), data=req_body)
        return self.send_request( req )

    def update_email(self, email):
        req_body = {
            "email": str(email)
            }
        req = requests.Request("PUT", render_url('/users'), data=req_body)
        return self.send_request( req )

    def update_phone_code(self, phone, code):
        req_body = {
            "phone": str(phone),
            "code": str(code)
            }
        req = requests.Request("PUT", render_url('/users'), data=req_body)
        return self.send_request( req )

    def update_phone_pwd_code(self, phone, pwd, code):
        req_body = {
            "phone": str(phone),
            "password": str(pwd),
            "code": str(pwd)
            }
        req = requests.Request("PUT", render_url('/users'), data=req_body)
        return self.send_request( req )

    # password reset
    def password_reset(self, email):
        req_body = {
            "email": email
            }
        req = requests.Request("POST", render_url('/reset_password'), data=req_body)
        return self.send_request( req )

    def password_reset_with(self, phone, code, new_pwd):
        req_body = {
            "phone": phone,
            "code": code,
            "new_pwd": new_pwd
            }
        req = requests.Request("PUT", render_url('/reset_password'), data=req_body)
        return self.send_request( req )

    #===codes
    def get_code(self, phone):
        req_body = {'phone': phone}
        req = requests.Request("POST", render_url('/codes'), data=req_body)
        return self.send_request( req )

    def verify_code(self, phone, code):
        req_body = {'phone': phone, 'code': code}
        req = requests.Request("POST", render_url('/codes'), data=req_body)
        return self.send_request( req )

    # === device
    def get_device_last_data(self, did):
        ''' 获取设备最近一次上传的数据 '''
        url = render_url('/devdata/' + str(did) + '/latest')
        req = requests.Request("GET", url)
        return self.send_request( req )

    def get_device_histroy_data(self, did, start_ts=None, end_ts=None,
                                    entity=None, attr=None, limit=20, skip=0):
        ''' 获取设备历史数据 '''
        req_body = {'start_ts': start_ts,
                        'end_ts': end_ts,
                        'entity': entity,
                        'attr': attr,
                        'limit': limit,
                        'skip': skip
                        }
        url = render_url('/devdata/' + str(did))
        req = requests.Request("GET", url, params=req_body)
        return self.send_request( req )

    def retrieve_product_histroy_data(self, product_key, did=None,
                                      start_ts=None, end_ts=None,
                                      entity=None, attr=None,
                                      limit=20, skip=0):
        req_body = {
            'product_key': product_key,
            'did':did,
            'start_ts': start_ts,
            'end_ts': end_ts,
            'entity': entity,
            'attr': attr,
            'limit': limit,
            'skip': skip
            }
        url = render_url('/devdata')
        req = requests.Request("GET", url, params=req_body)
        return self.send_request( req )

    # bound device
    def bind_device(self, devices):
        '''
        :param devices: struct = > [('did', 'passcode', 'remark(optional)', ...]
            did:
                与product_key不同,指设备的ID(Device id)，当设备初次接入机智云时,
                机智云根据Productkey以及设备Wi-Fi模块MAC地址为设备注册一个全网唯一的did,
                用于设备的身份识别以及设备与用户的绑定及后续操作
            passcode:
                设备通行证，用于校验用户的绑定/控制权限.
                当用户发起设备绑定时，只要是合法操作即可拿到此通行证，通过此通行证绑定设备并对设备进行有效期内的查看、控制等操作
            remark:
                用于设置备注信息
        :returns: Response
        '''
        req = g_bind_devices(devices)
        return self.send_request( req )

    def unbind_devices(self, devices):
        '''
        :param devices: struct = > [('did', 'passcode'), ...]
        '''
        req = g_unbind_devices(devices)
        return self.send_request( req )

    def control_device_by_attr(self, did, attr):
        '''
        设置数据点, 控制设备
        :param did: did. type: String
        :param attr: {'PC_OnOff':false, ...}. type: dict
        '''
        req_body = {
            'attrs': attr
            }
        url = render_url('/control/' + str(did))
        req = requests.Request("POST", url, data=req_body)
        return self.send_request( req )

    def control_device_by_raw(self, did, raw):
        '''
        发送原始指令, 控制设备
        :param did: did. type: String
        :param raw: struct => [<byte>, <byte>, ...]. type: list
        '''
        req_body = {
            'raw': raw
            }
        url = render_url('/control/' + str(did))
        req = requests.Request("POST", url, data=req_body)
        return self.send_request( req )

    @auto_resp_json
    def get_bind_device(self, limit=20, skip=0):
        ''' 获取绑定列表 '''
        req_body = {
            'limit': limit,
            'skip': skip
            }
        req = requests.Request("GET", render_url('/bindings'), params=req_body)
        return self.send_request( req )

    def device_detail(self, did):
        url = render_url('/devices/' + str(did))
        req = requests.Request("GET", url)
        return self.send_request( req )
    
    def query_device(self, product_key, mac):
        req_body = {
            'product_key': product_key,
            'mac':mac
            }
        req = requests.Request("GET", render_url('/devices'), params=req_body)
        return self.send_request( req )

    def create_scheduler(self, time, repeat, retry_count, retry_task, tasks, date=None):
        '''
        http://site.gizwits.com/document/m2m/openapi/#appschedulerlimitskip
        :param date: required if repeat is 'none'
        '''
        req = g_create_scheduler(date, time, repeat, retry_count, retry_task, tasks)
        resp = self.send_request( req )
        logging.debug(resp.content)
        return resp

    def fetch_scheduler(self, skip=None, limit=None):
        '''
        http://site.gizwits.com/document/m2m/openapi/#get_4
        :param limit: (optional)
        :param skip: (optional)
        '''
        req_body = {'limit':limit, 'skip':skip}
        url = render_url('/scheduler')
        req = requests.Request("GET", url, params=req_body)
        resp = self.send_request( req )
        logging.debug(resp.content)
        return resp

    def del_scheduler(self, sid):
        '''
        http://site.gizwits.com/document/m2m/openapi/#delete_1
        :param sid: scheduler id
        '''
        url = render_url('/scheduler/{0}'.format(sid))
        req = requests.Request("DELETE", url)
        resp = self.send_request( req )
        logging.debug(resp.content)
        return resp

    def scheduler_logs(self, sid):
        '''
        http://site.gizwits.com/document/m2m/openapi/#get_5
        :param sid: scheduler id
        '''
        url = render_url('/scheduler/{0}/logs'.format(sid))
        req = requests.Request("GET", url)
        resp = self.send_request( req )
        logging.debug(resp.content)
        return resp


# g_users.py
def g_create_user_by_auth_data(auth_data):
    '''
    :param auth_data: struct => {'src':'baidu|sina|qq', 'uid':'2346677','token':'pnktnjyb996sj4p156gjtp4im'}
    :type: dict
    '''
    req_body = {'authData': auth_data}
    return requests.Request("POST", render_url('/users'), data=req_body)

# g_codes.py
def g_bind_devices(devices):
    '''
    :param devices: struct = > [('did', 'passcode', 'remark(optional)', ...]
    '''
    data_devices = []
    for d in devices:
        device = {'did': None, 'passcode': None}
        device['did'] = d[0]
        device['passcode'] = d[1]
        try:
            device['remark'] = d[2]
        except IndexError:
            pass
        data_devices.append(device)
    req_body = {'devices':data_devices}
    return requests.Request("POST", render_url('/bindings'), data=req_body)

def g_unbind_devices(devices):
    data_devices = []
    for did, passcode in devices:
        device = {'did': None, 'passcode': None}
        device['did'] = did
        device['passcode'] = passcode
        data_devices.append(device)
    req_body = {'devices':data_devices}
    return requests.Request("DELETE", render_url('/bindings'), data=req_body)

def g_create_scheduler(date, time, repeat, retry_count, retry_task, tasks):
    '''
    :param date: required if repeat is 'none'
    :param time: '2014-11-11'
    :param repeat: "none" or "mon", "tue", "wed", "thu", "fri", "sat", "sun"
    :param retry_count: retry totals
    :param retry_task: "all" or "failed"
    :param tasks: 
        struct => {
        "did": "did1",
        "product_key": "xxx",
            "attrs": {
            "attr1": val,
            "attr2": val
            }
        }
    '''
    req_body = {
        "time": time,
        "repeat": repeat,
        "task": tasks,
        "retry_count": retry_count,
        "retry_task": retry_task
        }
    if date:
        req_body['date'] = date
    url = render_url('/scheduler')
    logging.debug(url)
    logging.debug(req_body)
    return requests.Request("POST", url, data=req_body)

if __name__ == "__main__":
    def testStep():
        username, password, appid, mac = GetConfig( './etc/gokit.conf', 'email', keys=['username', 'password', 'appid', 'mac'] )
        # setp 1
        gsc = GServiceClient(appid=appid)
        res = gsc.login(username, password)
        print res

        # setp 2
        res = gsc.get_bind_device(limit=20, skip=0)
        print res

        # setp 2.1
        did = None
        for device in res['devices']:
            if device['mac'] == mac:
                did = device['did']
                break
        if did is None: print 'did is None', sys.exit()
        else: print 'did', did

        '''
        # setp 3
        attr = {'PC_OnOff':False}
        res = gsc.control_device_by_attr(did, attr)
        print res.content
        '''

        # setp 4
        res = gsc.get_device_last_data(did)
        print res.content

if __name__ == "__main__":
    def Usage():
        print """
        ./gokit_openapi.py testStep
        """
    import sys, os, pdb
    if len(sys.argv)==1: Usage(), sys.exit()
    function = sys.argv[1]
    del sys.argv[1]
    eval(function)()
 
